import json
import os
from PyQt5.QtWidgets import (
    QWidget,
    QScrollArea,
    QHBoxLayout,
    QVBoxLayout,
    QLabel,
    QStyledItemDelegate,
    QDesktopWidget,
    QPushButton,
    QFileDialog,
    QComboBox,
    QFrame,
    QScrollArea,
    QAbstractItemView,
    QTableWidgetItem,
    QTableWidget,
    QSpacerItem,
    QSizePolicy,
)
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QEvent
from PyQt5.QtGui import QPainter, QPen, QColor, QDoubleValidator
from pydantic import BaseModel, Field, field_validator
import gui.controls.envs as envs

from ..basic_grid_component import BasicGridComponent

# import gui.controls.envs as envs
from ..dlg_model_params import DlgModelParams
from ..c_params_table import CParamsTable
from ..dlg_params import DlgParams
from ..utils import Utils
from ..custom_widget import LineEditFloat, LineEditStr, LineEditInt, SelectBox
from ..util import util_key_map

from enum import Enum


class FloatValidatorDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super(FloatValidatorDelegate, self).__init__(parent)
        self.validator = QDoubleValidator(self)

    def createEditor(self, parent, option, index):
        editor = super(FloatValidatorDelegate, self).createEditor(parent, option, index)
        editor.setValidator(self.validator)
        return editor


class FineLine(QFrame):
    def __init__(self, parent=None):
        super(FineLine, self).__init__(parent)
        self.setFrameShape(QFrame.HLine)
        self.setFrameShadow(QFrame.Sunken)

    def paintEvent(self, event):
        painter = QPainter(self)
        color = QColor(255, 255, 255, 127)  # 透明度范围0-255，127为半透明

        painter.setPen(QPen(color, 0.5))  # 设置画笔宽度为0.5像素
        painter.drawLine(0, self.height() / 2, self.width(), self.height() / 2)


class CustomTable(CParamsTable):

    def __init__(self, rows=0, columns=2, parent=None):
        super(CustomTable, self).__init__(rows=rows, columns=columns, parent=parent)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.verticalHeader().setVisible(True)


class StrEnumWithAlias(str, Enum):
    def __new__(cls, value, alias):
        obj = str.__new__(cls, value)
        obj._value_ = value
        obj.alias = alias
        return obj


class DamageTypeEnum(StrEnumWithAlias):
    t1 = "复材表面损伤", "复材表面损伤"
    t2 = "蜂窝芯子损伤", "蜂窝芯子损伤"
    t3 = "贯穿式损伤", "贯穿式损伤"


class layerTypeEnum(StrEnumWithAlias):
    t1 = "原铺层", "原铺层"
    t2 = "加强层", "加强层"


# 定义 Pydantic 模型
class ParamsModelingData(BaseModel):
    presetModelPath: str = Field(
        description="预设模型路径",
        alias="presetModelPath",
        required=True,
        extra={
            "button": [{"name": "importPresetModel", "title": "浏览...", "width": 70}],
            "hide": True,
        },
    )
    damageCount: int = Field(
        description="损伤数量",
        alias="damageCount",
        required=True,
    )
    damageType: DamageTypeEnum = Field(
        description="损伤类型",
        alias="damageType",
        required=True,
    )
    damageCenterX: float = Field(
        description="损伤中心位置x",
        alias="damageCenterX",
        required=True,
    )
    damageCenterY: float = Field(
        description="损伤中心位置y",
        alias="damageCenterY",
        required=True,
    )
    damageCenterZ: float = Field(
        description="损伤中心位置z",
        alias="damageCenterZ",
        required=True,
    )
    honeycombCoreHeight: float = Field(
        description="蜂窝芯子高度",
        alias="honeycombCoreHeight",
        required=True,
    )
    damageExcisionDiameter: float = Field(
        description="损伤挖除直径",
        alias="damageExcisionDiameter",
        required=True,
    )
    submodelSizeL: float = Field(
        description="子模型尺寸长度",
        alias="submodelSizeL",
        required=True,
    )
    submodelSizeW: float = Field(
        description="子模型尺寸宽度",
        alias="submodelSizeW",
        required=True,
    )
    lapWidth: float = Field(
        description="搭接宽度",
        alias="lapWidth",
        required=True,
    )
    adhesiveLayerThickness: float = Field(
        description="胶层厚度",
        alias="adhesiveLayerThickness",
        required=True,
    )
    additionalExcisionSize: float = Field(
        description="额外挖除尺寸",
        alias="additionalExcisionSize",
        required=True,
    )
    lowerAdhesiveLayersCount: float = Field(
        description="下铺层修补胶层层数",
        alias="lowerAdhesiveLayersCount",
        required=True,
    )
    #
    topPlyNumber: int = Field(
        description="上铺层层数",
        alias="topPlyNumber",
        required=True,
        extra={"hide": True},
    )
    bottomPlyNumber: int = Field(
        description="下铺层层数",
        alias="bottomPlyNumber",
        required=True,
        extra={"hide": True},
    )
    topRepairPlyNumber: int = Field(
        description="上铺层修补层层数",
        alias="topRepairPlyNumber",
        required=True,
        extra={"hide": True},
    )
    bottomRepairPlyNumber: int = Field(
        description="下铺层修补层层数",
        alias="bottomRepairPlyNumber",
        required=True,
        extra={"hide": True},
    )


class ParamsModelingComponent(BasicGridComponent):
    detailParams_modelShow_signal = pyqtSignal(object)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.gridLayout.setContentsMargins(0, 0, 0, 0)

    def get_model(self):
        return ParamsModelingData

    def on_import_clicked(self):
        # 创建QFileDialog对话框实例
        file_dialog = QFileDialog()
        file_dialog.setNameFilter("CAE files (*.cae)")

        # 设置对话框标题
        file_dialog.setWindowTitle("选择预设模型文件")
        # 显示对话框并获取用户选择的文件
        if file_dialog.exec_():
            # 用户点击了“打开”或“保存”，获取选中的文件路径
            file_path = file_dialog.selectedFiles()[0]
            self.gridWidgets["presetModelPath"].setText(file_path)

    def setDefault(self):
        self.gridWidgets["damageCount"].setText("1")

    def on_topRepairPlyNumber_changed(self, key):
        if (
            self.gridWidgets["topPlyNumber"].text()
            and self.gridWidgets["topRepairPlyNumber"].text()
        ):
            try:
                topPlyNumber = float(self.gridWidgets["topPlyNumber"].text())
                topRepairPlyNumber = float(
                    self.gridWidgets["topRepairPlyNumber"].text()
                )
                if topRepairPlyNumber < topPlyNumber:
                    self.gridWidgets[key].setText("")
                    envs.MBox.warning("上铺层修补件层数不可小于上铺层层数！")
                    return
            except ValueError:
                pass

    def on_damageType_changed(self):
        if self.gridWidgets["damageType"].currentText() == "贯穿式损伤":
            self.gridWidgets["additionalExcisionSize"].setDisabled(False)
            self.gridWidgets["lowerAdhesiveLayersCount"].setDisabled(False)
            self.gridWidgets["bottomRepairPlyNumber"].setDisabled(False)
            self.gridWidgets["bottomRepairPlyTable"].setDisabled(False)
        else:
            self.gridWidgets["additionalExcisionSize"].setDisabled(True)
            self.gridWidgets["lowerAdhesiveLayersCount"].setDisabled(True)
            self.gridWidgets["bottomRepairPlyNumber"].setDisabled(True)
            self.gridWidgets["bottomRepairPlyTable"].setDisabled(True)

    def updateUi(self, fillData):

        data = {}
        data = util_key_map.convertKey(fillData, 0)

        for k, v in data.items():
            if self.gridWidgets.get(k) is not None:
                if isinstance(self.gridWidgets[k], QTableWidget):
                    self.loadTableData(self.gridWidgets[k], v)
                elif isinstance(self.gridWidgets[k], QComboBox):
                    self.gridWidgets[k].setCurrentText(v)
                else:
                    self.gridWidgets[k].setValue(v)

    def loadTableData(self, table, data):
        for index, item in enumerate(data):
            if "铺层名称" in item:
                table.addRow([item["铺层名称"], item["厚度"]])
            else:
                table.addRow([item["厚度"]])
        table.set_columns_editable()

    def updateUI(self):
        fileds = self.model.model_fields
        flagRow = 0
        for row, (field_name, field_info) in enumerate(fileds.items()):

            buttonList = []
            disable = False
            hide = False
            if field_info.json_schema_extra.get(
                "extra"
            ) and field_info.json_schema_extra.get("extra").get("hide"):
                hide = field_info.json_schema_extra.get("extra").get("hide")
            if field_info.json_schema_extra.get(
                "extra"
            ) and field_info.json_schema_extra.get("extra").get("button"):
                buttonList = field_info.json_schema_extra.get("extra").get("button")
            if field_info.json_schema_extra.get(
                "extra"
            ) and field_info.json_schema_extra.get("extra").get("disable"):
                disable = field_info.json_schema_extra.get("extra").get("disable")
            if hide:
                flagRow = flagRow + 1
                continue
            row = row - flagRow
            label = QLabel(field_info.description)
            self.gridLayout.addWidget(label, 2 * row, 0)
            if issubclass(field_info.annotation, Enum):
                self.gridWidgets[field_name] = SelectBox(
                    options=[
                        {"title": item.alias, "value": item.alias}
                        for item in list(field_info.annotation)
                    ]
                )
                self.gridWidgets[field_name].setMinimumWidth(100)
            else:
                field_type = field_info.annotation
                if field_type is float:
                    widget = LineEditFloat()
                elif field_type is int:
                    widget = LineEditInt()
                else:
                    widget = LineEditStr()
                if not disable:
                    widget.setPlaceholderText(f"请输入{field_info.description}")
                self.gridWidgets[field_name] = widget
            if disable:
                self.gridWidgets[field_name].setEnabled(False)
            self.gridLayout.addWidget(self.gridWidgets[field_name], 2 * row, 1)
            if row != 0:
                spacer = QSpacerItem(
                    20, 100, QSizePolicy.Minimum, QSizePolicy.Expanding
                )
                self.gridLayout.addItem(
                    spacer, 2 * row - 1, 0, 1
                )  # 在第1行添加一个占位符，占据第0列和第1列
            for i, btnItem in enumerate(buttonList):
                self.gridWidgets[btnItem.get("name")] = QPushButton(
                    btnItem.get("title")
                )
                self.gridWidgets[btnItem.get("name")].setMinimumWidth(
                    btnItem.get("width")
                )
                self.gridWidgets[btnItem.get("name")].setFixedHeight(30)
                self.gridLayout.addWidget(
                    self.gridWidgets[btnItem.get("name")], 2 * row, 2 + i
                )
        self.setLayout(self.gridLayout)


class DlgParamsModeling(DlgModelParams):
    paramsModeling_save_signal = pyqtSignal(object)

    def __init__(self, parent=None):
        super(DlgParamsModeling, self).__init__(parent)

        self.setWindowTitle("参数化建模")
        self.pBtnOK.setFocusPolicy(Qt.NoFocus)
        self.pBtnCancel.setFocusPolicy(Qt.NoFocus)
        self.setMinimumWidth(800)
        layout = QVBoxLayout()
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        componentLayout = QHBoxLayout()

        self.component = ParamsModelingComponent()
        self.component.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)

        componentLayout.addWidget(self.component)

        topLayout = QHBoxLayout()
        label = QLabel("预设模型路径")
        self.component.gridWidgets["presetModelPath"] = LineEditStr()
        self.component.gridWidgets["presetModelPath"].setPlaceholderText(
            "请选择预设模型路径"
        )
        self.component.gridWidgets["importPresetModel"] = QPushButton("浏览...")
        self.component.gridWidgets["importPresetModel"].setFocusPolicy(Qt.NoFocus)
        topLayout.addWidget(label)
        topLayout.addWidget(self.component.gridWidgets["presetModelPath"])
        topLayout.addWidget(self.component.gridWidgets["importPresetModel"])
        topLayout.setContentsMargins(-1, -1, -1, 15)

        self.component.gridWidgets["importPresetModel"].clicked.connect(
            self.component.on_import_clicked
        )

        topPlyLayers = self.createLayersTable("上铺层", "topPly")
        topRepairPlyLayers = self.createLayersTable("上铺层修补层", "topRepairPly", 1)
        bottomPlyLayers = self.createLayersTable("下铺层", "bottomPly")
        bottomRepairPlyLayers = self.createLayersTable(
            "下铺层修补层", "bottomRepairPly", 1
        )

        self.component.gridWidgets["topPlyNumber"].editingFinished.connect(
            lambda: self.component.on_topRepairPlyNumber_changed("topPlyNumber")
        )
        self.component.gridWidgets["topRepairPlyNumber"].editingFinished.connect(
            lambda: self.component.on_topRepairPlyNumber_changed("topRepairPlyNumber")
        )

        vLayout2 = QVBoxLayout()
        vLayout2.addLayout(topPlyLayers)
        vLayout2.addLayout(bottomPlyLayers)
        vLayout2.addLayout(topRepairPlyLayers)
        vLayout2.addLayout(bottomRepairPlyLayers)

        componentLayout.addItem(
            QSpacerItem(10, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        )
        componentLayout.addLayout(vLayout2)

        layout.addLayout(topLayout)
        layout.addLayout(componentLayout)

        self.a = QWidget()
        self.a.setLayout(layout)
        self.size = self.a.sizeHint()
        scroll.resize(self.size)
        self.a.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        scroll.setWidget(self.a)

        self.vbox.insertWidget(0, scroll)

        self.component.gridWidgets["damageType"].currentIndexChanged.connect(
            self.component.on_damageType_changed
        )
        self.component.setDefault()

        # jsonPath = "data/ipimas/uis/ParamsModeling.json"
        # if not os.path.exists(jsonPath):
        #     data = Utils.read_json("data/ipimas/uis/ParamsModeling.json")
        # else:
        #     data = Utils.read_json(jsonPath)
        # self.updateUi(data)
        # self.component.on_damageType_changed()

        # left, top, right, bottom = self.vbox.getContentsMargins()
        # self.resize(
        #     a.width(),
        #     a.height()
        #     + self.pBtnCancel.sizeHint().height()
        #     + top
        #     + bottom
        #     + 15
        #     + 2
        #     + 10,
        # )

        # available_height = QDesktopWidget().availableGeometry().height() - 50
        # if self.height() > available_height:
        #     self.resize(self.width(), available_height)

    def init(self, data):
        self.updateUi(data)
        self.component.on_damageType_changed()
        left, top, right, bottom = self.vbox.getContentsMargins()
        self.resize(
            self.a.width(),
            self.a.height()
            + self.pBtnCancel.sizeHint().height()
            + top
            + bottom
            + 15
            + 2
            + 10,
        )

        available_height = QDesktopWidget().availableGeometry().height() - 50
        if self.height() > available_height:
            self.resize(self.width(), available_height)

        # self.center_dialog()

    def onKeyPress(self, event):
        if event.key() in (Qt.Key_Return, Qt.Key_Enter):
            # 忽略回车键的默认行为
            event.ignore()
            # 将焦点移至表格的第一项
            # self.table_widget.setFocus()
            # self.table_widget.setCurrentCell(0, 0)  # 设置焦点到表格的第一项
        else:
            # 允许其他按键的默认行为
            event.accept()

    def onLineEditReturnPressed(self):
        pass
        # 当回车键被按下时，将焦点移至表格的第一项
        # self.table_widget.setFocus()
        # self.table_widget.setCurrentCell(0, 0)  # 设置焦点到表格的第一项

    def createLayersTable(self, title, name, type=0):
        numLayout = QHBoxLayout()
        label = QLabel(f"{title}层数")
        label.setFixedWidth(160)
        edit = LineEditInt()
        edit.setPlaceholderText("请输入层数")
        edit.editingFinished.connect(lambda: self.on_text_changed(flag=name))
        # edit.focusOutEvent =  partial(self.on_focus_out, name)
        self.component.gridWidgets[f"{name}Number"] = edit
        numLayout.addWidget(label)
        numLayout.addWidget(edit)
        numLayout.addStretch(1)
        layersLayout = QVBoxLayout()
        if type == 1:
            table = CustomTable(rows=1, columns=2)
            table.setItemDelegateForColumn(1, FloatValidatorDelegate(table))
            table.setHorizontalHeaderLabels(["铺层序号/名称", "厚度"])
        else:
            table = CustomTable(rows=1, columns=1)
            table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
            table.setHorizontalHeaderLabels(["厚度"])
        self.component.gridWidgets[f"{name}Table"] = table
        layersLayout.addLayout(numLayout)
        layersLayout.addWidget(table)

        # 计算前三行的高度
        total_height = 0
        total_height = table.rowHeight(0) * 3
        horizontal_header_height = table.horizontalHeader().height()
        # table.setFixedHeight(total_height + horizontal_header_height)
        table.resize(-1, total_height + horizontal_header_height)
        # print("111=",total_height + horizontal_header_height)
        table.setMinimumHeight(total_height + horizontal_header_height)
        table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)
        # table.setMinimumHeight(total_height + horizontal_header_height)
        # table.setMaximumHeight(total_height + horizontal_header_height)
        table.removeRow(0)  # 删除第一行
        return layersLayout

    def on_focus_out(self, event, extra_param):
        num = self.component.gridWidgets[f"{extra_param}Number"].text()
        self.update_table_rows(self.component.gridWidgets[f"{extra_param}Table"], num)

    def on_text_changed(self, flag):
        # print("flag=", flag)
        # flagEdit = f"{flag}Number"
        # if (
        #     (flagEdit == "topPlyNumber" or flagEdit == "topRepairPlyNumber")
        #     and self.component.gridWidgets["topPlyNumber"].text()
        #     and self.component.gridWidgets["topRepairPlyNumber"].text()
        # ):
        #     try:
        #         topPlyNumber = float(self.component.gridWidgets["topPlyNumber"].text())
        #         topRepairPlyNumber = float(
        #             self.component.gridWidgets["topRepairPlyNumber"].text()
        #         )
        #         if topRepairPlyNumber < topPlyNumber:
        #             envs.MBox.warning("上铺层修补件层数不可小于上铺层层数！")
        #             self.component.gridWidgets[flagEdit].setText("")
        #             return
        #     except ValueError:
        #         pass

        num = self.component.gridWidgets[f"{flag}Number"].text()
        self.update_table_rows(self.component.gridWidgets[f"{flag}Table"], num)

    def update_table_rows(self, table, num):
        try:
            num_rows = int(num)
            table.setRowCount(num_rows)
            table.set_columns_editable()
        except ValueError:
            table.setRowCount(0)

    def getTableDict(self, table, valueList, type):
        for i in range(table.rowCount()):
            rowDict = {}
            typeList = ["铺层名称", "厚度"]
            for j in range(table.columnCount()):

                item = table.item(i, j)
                if type == 1:
                    if j == 1:
                        rowDict[typeList[j]] = toFloat(item.text())
                    else:
                        rowDict[typeList[j]] = item.text()
                elif type == 0:
                    rowDict["厚度"] = toFloat(item.text())
            valueList.append(rowDict)
        return valueList

    @pyqtSlot()
    def on_pBtnOK_clicked(self):
        self.pBtnOK.setFocus()
        for flag in ["topPly", "topRepairPly", "bottomPly", "bottomRepairPly"]:
            self.on_text_changed(flag=flag)

        valueRes = self.component.getValues()

        valueRes["topPlyTable"] = []
        valueRes["bottomPlyTable"] = []
        valueRes["topRepairPlyTable"] = []
        valueRes["bottomRepairPlyTable"] = []

        self.getTableDict(
            self.component.gridWidgets["topPlyTable"],
            valueRes["topPlyTable"],
            0,
        )
        self.getTableDict(
            self.component.gridWidgets["bottomPlyTable"],
            valueRes["bottomPlyTable"],
            0,
        )
        self.getTableDict(
            self.component.gridWidgets["topRepairPlyTable"],
            valueRes["topRepairPlyTable"],
            1,
        )
        self.getTableDict(
            self.component.gridWidgets["bottomRepairPlyTable"],
            valueRes["bottomRepairPlyTable"],
            # descriptionRes["下铺层修补层"],
            1,
        )

        if self.component.gridWidgets["damageType"].currentText() != "贯穿式损伤":
            valueRes.pop("additionalExcisionSize", None)
            valueRes.pop("lowerAdhesiveLayersCount", None)
            valueRes.pop("bottomRepairPlyNumber", None)
            valueRes.pop("bottomRepairPlyTable", None)

        if valueRes["topPlyNumber"] and valueRes["topRepairPlyNumber"]:
            try:
                topPlyNumber = float(valueRes["topPlyNumber"])
                topRepairPlyNumber = float(valueRes["topRepairPlyNumber"])
                if topRepairPlyNumber < topPlyNumber:
                    return envs.MBox.warning("上铺层修补件层数不可小于上铺层层数！")
            except ValueError:
                pass

        if not self.check_empty_values(valueRes["topPlyTable"]):
            return envs.MBox.warning("请补全上铺层数据")
        if not self.check_empty_values(valueRes["bottomPlyTable"]):
            return envs.MBox.warning("请补全下铺层数据")
        if not self.check_empty_values(valueRes["topRepairPlyTable"]):
            return envs.MBox.warning("请补全上铺层修补层数据")
        if (
            self.component.gridWidgets["damageType"].currentText() == "贯穿式损伤"
        ) and not self.check_empty_values(valueRes["bottomRepairPlyTable"]):
            return envs.MBox.warning("请补全下铺层修补层数据")

        res = util_key_map.convertKey(valueRes, 1)

        file = "data/ipimas/uis/ParamsModeling.json"
        Utils.write_json(file, res)
        if hasattr(envs, "ipimas_updateParamsodeling"):
            envs.ipimas_updateParamsodeling(res)
        self.close()
        self.paramsModeling_save_signal.emit(res)

    def check_empty_values(self, data_list):
        for item in data_list:
            for key, value in item.items():
                if value == "" or value is None:
                    return False
                #     print(f"属性 '{key}' 的值为空或空字符串")
                # else:
                #     print(f"属性 '{key}' 的值为 '{value}'")
        return True

    def is_table_filled(self, table):
        # 遍历表格中的每个单元格
        for row in range(self.table.rowCount()):
            for column in range(self.table.columnCount()):
                item = self.table.item(row, column)
                # 检查单元格是否为空
                if not item or item.text().strip() == "":
                    return False  # 如果找到空单元格，返回False
        return True  # 所有单元格都已填写

    def closeEvent(self, event):
        DlgParamsModeling._shown = False  # 使用类名访问类属性
        DlgParamsModeling._instance = None  # 清除实例引用
        event.accept()


def toFloat(value):
    try:
        return float(value)
    except ValueError:
        return None
